﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace LA6
{
    /// <summary>
    /// The class of the main form in the program.
    /// </summary>
    public partial class SentenceGenerator : Form
    {
        private LinkedList rules = new LinkedList();
        private LinkedList usedRules = new LinkedList();
        private Stack stack = new Stack();

        /// <summary>
        /// The default constructor.
        /// </summary>
        public SentenceGenerator()
        {
            InitializeComponent();
        }

        /// <summary>
        /// The method called when the Select Grammar button is clicked.
        /// It allows the user to select the input file to load the grammar rules from.
        /// It then reads the rules into a linked list and displays them in the text box.
        /// </summary>
        /// <param name="sender">The sender of the message.</param>
        /// <param name="e">Event arguments</param>
        private void btnSelectGrammar_Click(object sender, EventArgs e)
        {
            // create and show dialog box enabling user to open file
            DialogResult result;
            string fileName;
            StreamReader fileReader;
            
            using (OpenFileDialog fileChooser = new OpenFileDialog())
            {
                result = fileChooser.ShowDialog();
                fileName = fileChooser.FileName;

            }// end using

            // check user clicked Open
            if (result == DialogResult.OK)
            {
                if (fileName == string.Empty)
                    MessageBox.Show("Invalid File Name", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                else
                {
                    try
                    {
                        //Create FileStream to obtain read access to file
                        FileStream input = new FileStream(fileName, FileMode.Open, FileAccess.Read);

                        //Set file from where data is read
                        fileReader = new StreamReader(input);
                        char[] delimiters = { ' ', '-', '>' };

                        while (!fileReader.EndOfStream)
                        {
                            string line = fileReader.ReadLine();
                            string[] components = line.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
                            string[] rulesArray = new string[components.Length - 1];
                            Array.Copy(components, 1, rulesArray, 0, components.Length - 1);

                            rules.InsertAtBack(components[0], rulesArray);
                        }

                        txtRules.Text = rules.ToString();
                    }

                    catch (IOException)
                    {
                        MessageBox.Show("Error reading from file", "File Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        /// <summary>
        /// The method called when the Generate button is clicked.
        /// It chooses whether to call GenerateSingleSentence or GenerateMultipleSentences based on the selected radio button.
        /// </summary>
        /// <param name="sender">The sender of the message.</param>
        /// <param name="e">Event Arguments</param>
        private void btnGenerate_Click(object sender, EventArgs e)
        {
            try
            {
                // If the single sentence radio button is chosen:
                if (radioSingleSentence.Checked)
                {
                    txtOutputSentence.Text = GenerateSingleSentence();
                    txtUsedRules.Text = usedRules.ToString();
                }

                // If the multiple sentence radio button is chosen:
                else if (radioMultipleSentences.Checked)
                {
                    GenerateMultipleSentences(int.Parse(txtNumberOfSentences.Text));
                    txtOutputSentence.Clear();
                    txtUsedRules.Text = string.Empty;
                }

                else
                    MessageBox.Show("Error generating sentence(s)", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            catch (FormatException)
            {
                MessageBox.Show("You must enter the number of sentences as an integer", "FormatException", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Generates a single sentence using the linked list of grammar rules.
        /// </summary>
        /// <returns>The generated sentence.</returns>
        private string GenerateSingleSentence()
        {
            stack.Push("S");
            string current;
            string[] rule = new string[0];
            string sentence = "";
            usedRules.Clear();

            while (true)
            {
                current = stack.Pop();

                // If nothing is left on the stack, break out of the loop
                if (current == null)
                    break;

                // Search through the array and find a rule
                rule = rules.Search(current);

                // Decide what to do with the rule:
                // If there is no rule to match it, it is a word and will be stored in the sentence
                if (rule == null)
                    sentence += current + " ";

                // If there is a matching rule, push the rule(s) on the stack
                else
                {
                    for (int x = (rule.Length - 1); x >= 0; x--)
                    {
                        stack.Push(rule[x]);
                    }

                    // Store the used rule in the usedRules linked list
                    usedRules.InsertAtBack(current, rule);
                }
            }

            // Capitalize the first letter of the sentence
            sentence = char.ToUpper(sentence[0]).ToString() + sentence.Substring(1, sentence.Length - 1);

            // Add a period to the end of the sentence
            sentence = sentence.Substring(0, sentence.Length - 1) + ".";

            return sentence;
        }

        /// <summary>
        /// Prompts the user for where to save the output file.
        /// Then generates the given number of sentences and writes them to the output file.
        /// </summary>
        /// <param name="numberOfSentences">The number of sentences to generate.</param>
        private void GenerateMultipleSentences(int numberOfSentences)
        {
            string[] sentences = new string[numberOfSentences];

            // Generate sentences and fill array
            for (int x = 0; x < numberOfSentences; x++)
            {
                sentences[x] = GenerateSingleSentence();
            }

            // Write array to output file:
            StreamWriter fileWriter;
            DialogResult result;
            string filename;

            using (SaveFileDialog fileChooser = new SaveFileDialog())
            {
                fileChooser.CheckFileExists = false;
                fileChooser.Filter = "Text File|*.txt|All|*.*";
                result = fileChooser.ShowDialog();
                filename = fileChooser.FileName;
            }

            if (result == DialogResult.OK)
            {
                if (filename == string.Empty)
                    MessageBox.Show("Invalid file name", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                else
                {
                    // Save file via FileStream if user specified valid file
                    try
                    {
                        FileStream output = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write);

                        fileWriter = new StreamWriter(output);

                        foreach (string s in sentences)
                        {
                            fileWriter.WriteLine(s);
                        }

                        fileWriter.Close();
                    }

                    catch (IOException)
                    {
                        MessageBox.Show("Error writing file", "IOException", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }

                MessageBox.Show("Output file written successfully!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
    }
}